-module(my_net).
-behaviour(gen_server).

-export([start_link/1, stop/0, init/1, terminate/2, code_change/3, handle_cast/2, handle_info/2, handle_call/3]).
-include("common.hrl").

-record(state, {sock = 0, sid = 0, ip = 0,acc_id = 0,room_hid = 0,step = 0,recv = 0,send=0, prev=0}).


start_link(Data) ->
	gen_server:start_link(?MODULE, Data, []).

stop() ->
	gen_server:cast(?MODULE, stop).

init(Sock) ->
	?debug("init,Sock = ~p",[Sock]),
	{ok, #state{sid = self(),sock = Sock,ip = get_ip(Sock)}}.

handle_info({tcp, _Socket, Data}, #state{sid = _Sid,recv = Recv} = State) ->
%% 	?debug("get sid = ~p,Data=~p",[_Sid,Data]),
	case Recv == 0 of	
		true ->
			Packet = Data;
		_ ->
			Packet = list_to_binary([ Recv, Data ])
	end,
	
	case parse(State, Packet) of
		{ok, Remain} -> 
			{noreply, State#state{recv = Remain}};
		_ -> 
			?log_error("msg parse error"),
			{stop, normal, State}
	end;

handle_info({tcp_closed, _Socket}, #state{acc_id = AccID} =State) ->
	?log_trace("tcp_closed AccID=~p,reson=~p",[AccID,"tcp closed"]),
	{stop, normal, State}.

terminate(_Reason, State) ->
	close_tcp(State),
	ok.

code_change(_OldVsn, State, _Extra) ->    
	{ok, State}.

handle_call(_Request, _From, State) ->    
	{reply, ok, State}.

handle_cast(flush, #state{sock = Socket, prev = Prev} = State) ->
	P = list_to_binary(lists:reverse(Prev)),
	gen_tcp:send(Socket, P),
	{noreply, State#state{send = 0, prev = 0}};

handle_cast({send, Data},State = #state{sid = _Sid,send = Send, prev = Prev}) ->
	NewData = case Data of
		<<  _Len:?u32,16#d005:?u16, _Remain/binary >> -> Data;
		<<  Len:?u32,Pt:?u16, Remain/binary >> ->
			Code = case get(psw_code) of
				PswCode when erlang:is_integer(PswCode) -> PswCode;
				_ -> 0
			end,
%% ?debug("send Len = ~p,code = ~p",[Len,Code]),
			NewLen = Len bxor Code,
%% ?debug("send NewLen = ~p,code = ~p",[NewLen,Code]),
			<<NewLen:?u32,Pt:?u16, Remain/binary >>;
		_ -> {error,Data}
	end,

	case Prev of
		0 ->
			P = [NewData];
		_ ->
			P = [NewData|Prev]
	end,
	
	case Send of
		0 ->
			timer:apply_after(20, gen_server, cast, [self(), flush]);
		_ ->
			ok
	end,
	{noreply, State#state{prev = P, send = 1}};

handle_cast({set_val, Aid, RoomHid, Step},  State) ->
	?debug("set_val,{Aid, RoomHid, Step} = ~p",[{Aid, RoomHid, Step}]),
	{noreply, State#state{acc_id = Aid, room_hid = RoomHid, step = Step}};
handle_cast({discon, Reason}, #state{acc_id = Aid,sock = Socket} = State) ->
	?log_warning("discon aid=~p,reson=~p,Socket=~p",[Aid,Reason,Socket]),
	gen_tcp:close(Socket),
	{stop, normal, State};

handle_cast(Msg,#state{acc_id = Aid} =State) ->
	?log_error("mynet can not match aid=~p,msg=~p",[Aid,Msg]),
	{noreply, State}.

parse(#state{sid = Sid,acc_id = Aid, room_hid = Hid, ip =Ip,step = Step} = State, Packet) ->
%% 	?debug("get sid = ~p,Packet=~p",[Sid,Packet]),
	case Packet of
		<<  Len:32/unsigned-integer, Remain/binary >> ->
			{Check,Code} = case get(psw_code) of
				PswCode when erlang:is_integer(PswCode) -> {true,PswCode};
				_ ->
					NewCode = util:rand(16#8000, 16#FFFF),

					PtCode = pt_net_chg_code_d005:new(),
					PtCode1 = PtCode#pt_net_chg_code{code = NewCode},
					?send(Sid, pt_net_chg_code_d005:to_binary(PtCode1)),
					put(psw_code,NewCode),
					{false,NewCode}
			end,
				

			BodyLen1 = case Check of
				true -> 
					Len bxor Code;
				_ -> Len
			end,
			BodyLen = BodyLen1 - ?HEADER_LENGTH,      
			if 
				BodyLen < ?MINI_BODY ->	?discon(Sid,'msg error minibody',400),{ok, 0};
				BodyLen > ?MAX_BODY  -> ?discon(Sid,'msg error maxbody',400),{ok, 0};
				true ->
					case Remain of
						<<Data:BodyLen/binary, Remain2/binary >> ->
	%% 							?debug("Step = ~p,Hid = ~p,Data = ~p",[Step,Hid,Data]),
							case Step of
								0 -> gen_server:cast({global, login}, {recv, Sid, Ip, Data});
								1 -> gen_server:cast({global, game_center}, {recv, Sid, Data});
								2 when erlang:is_pid(Hid) -> gen_server:cast(Hid, {recv, Sid, Data});
								_ -> ?log_error("mynet can not match aid=~p,Step=~p,Hid=~p",[Aid,Step,Hid])
							end,
							
							case byte_size(Remain2) > 0 of
								true ->
									parse(State, Remain2);
								_ ->
									{ok, 0}
							end;
						_ ->
							{ok, Packet}
					end
			end;
		_ -> {ok, Packet}        
	end.
close_tcp(#state{sid = Sid,acc_id = Aid}) -> 
	gen_server:cast({global, game_center}, {close_tcp,Sid,Aid}).

get_ip(Socket)->
	case inet:peername(Socket) of
		{ok,{Address,_Port}}->Address;
		_-> {0,0,0,0}
	end.